home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xpaint-2.1.1
/
fillOp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-03
|
8KB
|
325 lines
/* +-------------------------------------------------------------------+ */
/* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */
/* | | */
/* | Permission to use, copy, modify, and to distribute this software | */
/* | and its documentation for any purpose is hereby granted without | */
/* | fee, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. There is no | */
/* | representations about the suitability of this software for | */
/* | any purpose. this software is provided "as is" without express | */
/* | or implied warranty. | */
/* | | */
/* +-------------------------------------------------------------------+ */
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#ifndef NOSTDHDRS
#include <stdlib.h>
#include <unistd.h>
#endif
#include "Paint.h"
#include "misc.h"
#include "xpaint.h"
typedef struct {
Boolean done;
} LocalInfo;
#define ZINDEX8(x, y, img) ((y) * img->bytes_per_line) + (x)
static Drawable drawable;
static Display *dpy;
static XImage *source;
static Boolean *mask;
static int maskW, maskH;
static int xMin, yMin, xMax, yMax;
static int timeCount = 0;
static int fillMode = 0;
/*
* A Seed Fill Algorithm
* by Paul Heckbert
* from "Graphics Gems", Academic Press, 1990
*/
/*
* fill.c : simple seed fill program
* Calls pixelread() to read pixels, pixelwrite() to write pixels.
*
* Paul Heckbert 13 Sept 1982, 28 Jan 1987
*/
#define pixelwrite(x,y) do { \
mask[y * maskW + x] = True; \
xMin = MIN(xMin, x); \
yMin = MIN(yMin, y); \
xMax = MAX(xMax, x); \
yMax = MAX(yMax, y); \
} while (0)
#define STEP 32
static Pixel pixelread(int x, int y, Boolean first)
{
static Pixel ov;
static int xMin, yMin, xMax, yMax;
Pixel p;
int n;
if (first) {
xMin = MAX(x - STEP, 0);
yMin = MAX(y - STEP, 0);
xMax = MIN(x + STEP, maskW);
yMax = MIN(y + STEP, maskH);
XGetSubImage(dpy, drawable, xMin, yMin,
xMax - xMin, yMax - yMin,
AllPlanes, ZPixmap, source,
xMin, yMin);
ov = XGetPixel(source, x, y);
return ov;
} else if (mask[y * maskW + x]) {
return ~ov;
}
if (x < xMin) {
n = MAX(x - STEP, 0);
XGetSubImage(dpy, drawable, n, yMin,
xMin - n, yMax - yMin,
AllPlanes, ZPixmap, source,
n, yMin);
xMin = n;
}
if (y < yMin) {
n = MAX(y - STEP, 0);
XGetSubImage(dpy, drawable, xMin, n,
xMax - xMin, yMin - n,
AllPlanes, ZPixmap, source,
xMin, n);
yMin = n;
}
if (x >= xMax) {
n = MIN(x + STEP, maskW);
XGetSubImage(dpy, drawable, xMax, yMin,
n - xMax, yMax - yMin,
AllPlanes, ZPixmap, source,
xMax, yMin);
xMax = n;
}
if (y >= yMax) {
n = MIN(y + STEP, maskH);
XGetSubImage(dpy, drawable, xMin, yMax,
xMax - xMin, n - yMax,
AllPlanes, ZPixmap, source,
xMin, yMax);
yMax = n;
}
/*
** Do it fast for those 8 bit displays...
*/
if (source->bits_per_pixel == 8) {
p = source->data[ZINDEX8(x, y, source)];
} else {
p = XGetPixel(source, x, y);
}
return p;
}
/*
** Exange pixel 1 for filled pixel value 2
*/
static void change(int sx, int sy, int width, int height)
{
int x, y;
Pixel pix = XGetPixel(source, sx, sy);
if (source->bits_per_pixel == 8) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
if (source->data[ZINDEX8(x, y, source)] == pix)
pixelwrite(x, y);
if (y % 100 == 0)
StateTimeStep();
}
} else {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
if (XGetPixel(source, x, y) == pix)
pixelwrite(x, y);
if (y % 64 == 0)
StateTimeStep();
}
}
}
typedef struct {short y, xl, xr, dy;} Segment;
/*
* Filled horizontal segment of scanline y for xl<=x<=xr.
* Parent segment was on line y-dy. dy=1 or -1
*/
#define STACKSIZE 20000 /* max depth of stack */
#define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \
if (sp<stack+STACKSIZE && Y+(DY)>=0 && Y+(DY)<=height) \
{sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
#define POP(Y, XL, XR, DY) /* pop segment off stack */ \
{sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
/*
* fill: set the pixel at (x,y) and all of its 4-connected neighbors
* with the same pixel value to the new pixel value nv.
* A 4-connected neighbor is a pixel above, below, left, or right of a pixel.
*/
static void fill(int x, int y, int width, int height)
{
int start, x1, x2, dy = 0;
Pixel ov; /* old pixel value */
Segment stack[STACKSIZE], *sp = stack; /* stack of filled segments */
ov = pixelread(x, y, True); /* read pv at seed point */
PUSH(y, x, x, 1); /* needed in some cases */
PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */
while (sp>stack) {
/* pop segment off stack and fill a neighboring scan line */
POP(y, x1, x2, dy);
/*
* segment of scan line y-dy for x1<=x<=x2 was previously filled,
*/
for (x = x1; x >= 0 && pixelread(x, y, False) == ov; x--)
pixelwrite(x, y);
if (x >= x1) {
for (x++; x<=x2 && pixelread(x, y, False)!=ov; x++);
start = x;
if (x > x2)
continue;
} else {
start = x+1;
if (start<x1) PUSH(y, start, x1-1, -dy); /* leak on left? */
x = x1+1;
}
do {
for (; x<=width && pixelread(x, y, False)==ov; x++)
pixelwrite(x, y);
PUSH(y, start, x-1, dy);
if (x>x2+1) PUSH(y, x2+1, x-1, -dy); /* leak on right? */
for (x++; x<=x2 && pixelread(x, y, False)!=ov; x++);
start = x;
} while (x<=x2);
if (++timeCount % 32 == 0)
StateTimeStep();
}
}
static void press(Widget w, LocalInfo *l, XButtonEvent *event, OpInfo *info)
{
XRectangle undo, rect;
int width, height;
GC gc, dgc;
XGCValues values;
int isBusy, depth;
Pixmap pix;
if (event->button == Button1)
dgc = info->first_gc;
else if (event->button == Button2)
dgc = info->second_gc;
else
return;
dpy = XtDisplay(w);
drawable = info->drawable;
UndoStart(w, info);
XtVaGetValues(w, XtNdrawWidth, &width,
XtNdrawHeight, &height,
XtNdepth, &depth,
NULL);
if (isBusy = (width * height > 6400))
StateSetBusy(True);
if ((mask = (Boolean*)XtCalloc(sizeof(Boolean), width * height)) == NULL)
return;
maskW = width;
maskH = height;
memset(mask, False, width * height * sizeof(Boolean));
if (fillMode == 0) {
source = NewXImage(dpy, NULL, depth, width, height);
} else {
source = PwGetImage(w, NULL);
}
xMin = xMax = info->realX;
yMin = yMax = info->realY;
if (fillMode == 0)
fill(info->realX, info->realY, width - 1, height - 1);
else
change(info->realX, info->realY, width, height);
rect.x = xMin;
rect.y = yMin;
rect.width = (xMax - xMin) + 1;
rect.height = (yMax - yMin) + 1;
pix = MaskDataToPixmap(w, width, height, mask, &rect);
XSetClipOrigin(dpy, dgc, rect.x, rect.y);
XSetClipMask(dpy, dgc, pix);
XFillRectangles(dpy, info->drawable, dgc, &rect, 1);
if (!info->isFat)
XFillRectangles(dpy, XtWindow(w), dgc, &rect, 1);
XSetClipMask(dpy, dgc, None);
XFreePixmap(dpy, pix);
XYtoRECT(xMin, yMin, xMax + 1, yMax + 1, &undo);
UndoSetRectangle(w, &undo);
PwUpdate(w, &undo, False);
if (fillMode == 0)
XDestroyImage(source);
if (isBusy)
StateSetBusy(False);
}
void *FillAdd(Widget w)
{
LocalInfo *l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
l->done = False;
OpAddEventHandler(w, opPixmap, ButtonPressMask, FALSE, (OpEventProc)press, l);
SetCrossHairCursor(w);
return l;
}
void FillRemove(Widget w, LocalInfo *l)
{
OpRemoveEventHandler(w, opPixmap, ButtonPressMask, FALSE, (OpEventProc)press, l);
XtFree((XtPointer)l);
}
void FillSetMode(int value)
{
fillMode = value;
}
int FillGetMode()
{
return fillMode;
}